package cn.com.stary1993.kite.captcha.config;

import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.impl.CacheImageCaptchaGenerator;
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
import cloud.tianai.captcha.resource.ResourceStore;
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
import cloud.tianai.captcha.validator.impl.BasicCaptchaTrackValidator;
import cn.com.stary1993.kite.captcha.store.CacheStore;
import cn.com.stary1993.kite.captcha.store.impl.LocalCacheStore;
import cn.com.stary1993.kite.captcha.store.impl.RedisCacheStore;
import cn.com.stary1993.kite.captcha.support.behavior.*;
import cn.com.stary1993.kite.captcha.support.behavior.secondary.BehaviorImageCaptchaSecondaryVerification;
import cn.com.stary1993.kite.captcha.support.simple.DefaultSimpleImageCaptcha;
import cn.com.stary1993.kite.captcha.support.simple.SimpleImageCaptcha;
import cn.com.stary1993.kite.captcha.support.simple.generator.DefaultSimpleImageCaptchaGenerator;
import cn.com.stary1993.kite.captcha.support.simple.generator.SimpleImageCaptchaGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * @author stary1993
 * <p>
 * 验证码配置类
 */
@Slf4j
@Order
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ CaptchaProperties.class, SimpleImageCaptchaProperties.class,
		BehaviorImageCaptchaProperties.class })
@ConditionalOnProperty(prefix = CaptchaProperties.PREFIX, name = "enabled", matchIfMissing = true)
public class CaptchaAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnProperty(prefix = CaptchaProperties.PREFIX + "." + CaptchaProperties.SIMPLE, name = "enabled",
			havingValue = "true")
	public static class SimpleImageCaptchaConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public SimpleImageCaptcha simpleImageCaptcha(CacheStore cacheStore, CaptchaProperties prop,
				SimpleImageCaptchaGenerator simpleImageCaptchaGenerator) {
			return new DefaultSimpleImageCaptcha(cacheStore, prop, simpleImageCaptchaGenerator);
		}

		@Bean
		@ConditionalOnMissingBean
		public SimpleImageCaptchaGenerator simpleImageCaptchaGenerator(CaptchaProperties prop) {
			return new DefaultSimpleImageCaptchaGenerator(prop);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnProperty(prefix = CaptchaProperties.PREFIX + "." + CaptchaProperties.BEHAVIOR, name = "enabled",
			havingValue = "true")
	public static class BehaviorImageCaptchaConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public ResourceStore resourceStore(CaptchaProperties prop) {
			return new BehaviorImageCaptchaResourceStore(prop.getBehavior());
		}

		@Bean
		@ConditionalOnMissingBean
		public ImageCaptchaResourceManager imageCaptchaResourceManager(ResourceStore resourceStore) {
			return new DefaultImageCaptchaResourceManager(resourceStore);
		}

		@Bean
		@ConditionalOnMissingBean
		public ImageCaptchaGenerator imageCaptchaTemplate(CaptchaProperties prop,
				ImageCaptchaResourceManager captchaResourceManager, BeanFactory beanFactory) {
			// 构建多验证码生成器
			ImageCaptchaGenerator captchaGenerator = new SpringMultiBehaviorImageCaptchaGenerator(
					captchaResourceManager, beanFactory);
			BehaviorImageCaptchaProperties.CaptchaCacheProperties cache = prop.getBehavior().getCache();
			if (cache != null && cache.getEnabled() && cache.getCacheSize() > 0) {
				// 增加缓存处理
				captchaGenerator = new CacheImageCaptchaGenerator(captchaGenerator, cache.getCacheSize(),
						cache.getWaitTime(), cache.getPeriod());
			}
			// 初始化
			captchaGenerator.init(false);
			return captchaGenerator;
		}

		@Bean
		@ConditionalOnMissingBean
		public ImageCaptchaValidator imageCaptchaValidator(CaptchaProperties prop) {
			return new BasicCaptchaTrackValidator(prop.getBehavior().getDefaultTolerant());
		}

		@Bean
		@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
		@ConditionalOnMissingBean
		public BehaviorImageCaptchaCacheTemplateListener captchaTemplateListener() {
			return new BehaviorImageCaptchaCacheTemplateListener();
		}

		@Bean
		@ConditionalOnMissingBean
		public BehaviorImageCaptcha behaviorImageCaptcha(ImageCaptchaGenerator captchaGenerator,
				ImageCaptchaValidator imageCaptchaValidator, CacheStore cacheStore, CaptchaProperties prop) {
			BehaviorImageCaptcha target = new DefaultBehaviorImageCaptcha(captchaGenerator, imageCaptchaValidator,
					cacheStore, prop.getBehavior());
			if (prop.getBehavior().getSecondary() != null
					&& Boolean.TRUE.equals(prop.getBehavior().getSecondary().getEnabled())) {
				target = new BehaviorImageCaptchaSecondaryVerification(target, prop.getBehavior().getSecondary());
			}
			return target;
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(StringRedisTemplate.class)
	@Import({ RedisAutoConfiguration.class })
	@AutoConfigureAfter({ RedisAutoConfiguration.class })
	public static class RedisCacheStoreConfig {

		@Bean
		@ConditionalOnBean(StringRedisTemplate.class)
		@ConditionalOnMissingBean(CacheStore.class)
		public CacheStore redisCacheStore(StringRedisTemplate redisTemplate) {
			return new RedisCacheStore(redisTemplate);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@AutoConfigureAfter({ RedisCacheStore.class })
	@Import({ RedisCacheStore.class })
	public static class LocalCacheStoreConfig {

		@Bean
		@ConditionalOnMissingBean(CacheStore.class)
		public CacheStore localCacheStore() {
			return new LocalCacheStore();
		}

	}

}
